﻿using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;


namespace cmonitor.server.client.reports.screen.winapis
{
    public static class SECUR32
    {
        public enum WinStatusCodes : uint
        {
            STATUS_SUCCESS = 0
        }

        public enum WinErrors : uint
        {
            NO_ERROR = 0,
        }
        public enum WinLogonType
        {
            LOGON32_LOGON_INTERACTIVE = 2,
            LOGON32_LOGON_NETWORK = 3,
            LOGON32_LOGON_BATCH = 4,
            LOGON32_LOGON_SERVICE = 5,
            LOGON32_LOGON_UNLOCK = 7,
            LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
            LOGON32_LOGON_NEW_CREDENTIALS = 9
        }

        // SECURITY_LOGON_TYPE
        public enum SecurityLogonType
        {
            Interactive = 2,    // Interactively logged on (locally or remotely)
            Network,        // Accessing system via network
            Batch,          // Started via a batch queue
            Service,        // Service started by service controller
            Proxy,          // Proxy logon
            Unlock,         // Unlock workstation
            NetworkCleartext,   // Network logon with cleartext credentials
            NewCredentials,     // Clone caller, new default credentials
            RemoteInteractive,  // Remote, yet interactive. Terminal server
            CachedInteractive,  // Try cached credentials without hitting the net.
            CachedRemoteInteractive, // Same as RemoteInteractive, this is used internally for auditing purpose
            CachedUnlock    // Cached Unlock workstation
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct LSA_UNICODE_STRING
        {
            public UInt16 Length;
            public UInt16 MaximumLength;
            public IntPtr Buffer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct TOKEN_SOURCE
        {
            public TOKEN_SOURCE(string name)
            {
                SourceName = new byte[8];
                System.Text.Encoding.GetEncoding(1252).GetBytes(name, 0, name.Length, SourceName, 0);
                if (!ADVAPI32.AllocateLocallyUniqueId(out SourceIdentifier))
                    throw new System.ComponentModel.Win32Exception();
            }

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] SourceName;
            public IntPtr SourceIdentifier;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct KERB_INTERACTIVE_LOGON
        {
            public KERB_LOGON_SUBMIT_TYPE MessageType;
            public string LogonDomainName;
            public string UserName;
            public string Password;
        }
        public enum KERB_LOGON_SUBMIT_TYPE
        {
            KerbInteractiveLogon = 2,
            KerbSmartCardLogon = 6,
            KerbWorkstationUnlockLogon = 7,
            KerbSmartCardUnlockLogon = 8,
            KerbProxyLogon = 9,
            KerbTicketLogon = 10,
            KerbTicketUnlockLogon = 11,
            KerbS4ULogon = 12,
            KerbCertificateLogon = 13,
            KerbCertificateS4ULogon = 14,
            KerbCertificateUnlockLogon = 15
        }
        public enum TOKEN_INFORMATION_CLASS
        {
            /// <summary>
                /// The buffer receives a TOKEN_USER structure that contains the user account of the token.
                /// </summary>
            TokenUser = 1,

            /// <summary>
                /// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token.
                /// </summary>
            TokenGroups,

            /// <summary>
                /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token.
                /// </summary>
            TokenPrivileges,

            /// <summary>
                /// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects.
                /// </summary>
            TokenOwner,

            /// <summary>
                /// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects.
                /// </summary>
            TokenPrimaryGroup,

            /// <summary>
                /// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects.
                /// </summary>
            TokenDefaultDacl,

            /// <summary>
                /// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information.
                /// </summary>
            TokenSource,

            /// <summary>
                /// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token.
                /// </summary>
            TokenType,

            /// <summary>
                /// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails.
                /// </summary>
            TokenImpersonationLevel,

            /// <summary>
                /// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics.
                /// </summary>
            TokenStatistics,

            /// <summary>
                /// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token.
                /// </summary>
            TokenRestrictedSids,

            /// <summary>
                /// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token. 
                /// </summary>
            TokenSessionId,

            /// <summary>
                /// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token.
                /// </summary>
            TokenGroupsAndPrivileges,

            /// <summary>
                /// Reserved.
                /// </summary>
            TokenSessionReference,

            /// <summary>
                /// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag.
                /// </summary>
            TokenSandBoxInert,

            /// <summary>
                /// Reserved.
                /// </summary>
            TokenAuditPolicy,

            /// <summary>
                /// The buffer receives a TOKEN_ORIGIN value. 
                /// </summary>
            TokenOrigin,

            /// <summary>
                /// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token.
                /// </summary>
            TokenElevationType,

            /// <summary>
                /// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token.
                /// </summary>
            TokenLinkedToken,

            /// <summary>
                /// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated.
                /// </summary>
            TokenElevation,

            /// <summary>
                /// The buffer receives a DWORD value that is nonzero if the token has ever been filtered.
                /// </summary>
            TokenHasRestrictions,

            /// <summary>
                /// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token.
                /// </summary>
            TokenAccessInformation,

            /// <summary>
                /// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token.
                /// </summary>
            TokenVirtualizationAllowed,

            /// <summary>
                /// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token.
                /// </summary>
            TokenVirtualizationEnabled,

            /// <summary>
                /// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level. 
                /// </summary>
            TokenIntegrityLevel,

            /// <summary>
                /// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set.
                /// </summary>
            TokenUIAccess,

            /// <summary>
                /// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy.
                /// </summary>
            TokenMandatoryPolicy,

            /// <summary>
                /// The buffer receives the token's logon security identifier (SID).
                /// </summary>
            TokenLogonSid,

            /// <summary>
                /// The maximum value for this enumeration
                /// </summary>
            MaxTokenInfoClass
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct QUOTA_LIMITS
        {
            readonly UInt32 PagedPoolLimit;
            readonly UInt32 NonPagedPoolLimit;
            readonly UInt32 MinimumWorkingSetSize;
            readonly UInt32 MaximumWorkingSetSize;
            readonly UInt32 PagefileLimit;
            readonly Int64 TimeLimit;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct LSA_STRING
        {
            public UInt16 Length;
            public UInt16 MaximumLength;
            public /*PCHAR*/ IntPtr Buffer;
        }


        [DllImport("secur32.dll", SetLastError = true)]
        public static extern WinStatusCodes LsaLogonUser(
            [In] IntPtr LsaHandle,
            [In] ref LSA_STRING OriginName,
            [In] SecurityLogonType LogonType,
            [In] UInt32 AuthenticationPackage,
            [In] IntPtr AuthenticationInformation,
            [In] UInt32 AuthenticationInformationLength,
            [In] /*PTOKEN_GROUPS*/ IntPtr LocalGroups,
            [In] ref TOKEN_SOURCE SourceContext,
            [Out] /*PVOID*/ out IntPtr ProfileBuffer,
            [Out] out UInt32 ProfileBufferLength,
            [Out] out Int64 LogonId,
            [Out] out IntPtr Token,
            [Out] out QUOTA_LIMITS Quotas,
            [Out] out WinStatusCodes SubStatus
            );

        [DllImport("secur32.dll", SetLastError = true)]
        public static extern WinStatusCodes LsaRegisterLogonProcess(
            IntPtr LogonProcessName,
            out IntPtr LsaHandle,
            out ulong SecurityMode
            );

        [DllImport("secur32.dll", SetLastError = false)]
        public static extern WinStatusCodes LsaLookupAuthenticationPackage([In] IntPtr LsaHandle, [In] ref LSA_STRING PackageName, [Out] out UInt32 AuthenticationPackage);

        [DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [ResourceExposure(ResourceScope.None)]
        internal static extern int LsaConnectUntrusted(
                [In, Out] ref SafeLsaLogonProcessHandle LsaHandle);

        [DllImport("secur32.dll", SetLastError = false)]
        public static extern WinStatusCodes LsaConnectUntrusted([Out] out IntPtr LsaHandle);

        [System.Security.SecurityCritical]  // auto-generated
        internal sealed class SafeLsaLogonProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private SafeLsaLogonProcessHandle() : base(true) { }

            // 0 is an Invalid Handle
            internal SafeLsaLogonProcessHandle(IntPtr handle) : base(true)
            {
                SetHandle(handle);
            }

            internal static SafeLsaLogonProcessHandle InvalidHandle
            {
                get { return new SafeLsaLogonProcessHandle(IntPtr.Zero); }
            }

            [System.Security.SecurityCritical]
            protected override bool ReleaseHandle()
            {
                // LsaDeregisterLogonProcess returns an NTSTATUS
                return LsaDeregisterLogonProcess(handle) >= 0;
            }
        }

        [DllImport("secur32.dll", SetLastError = true)]
        [ResourceExposure(ResourceScope.None)]
        internal static extern int LsaDeregisterLogonProcess(IntPtr handle);


        public static void CreateNewSession()
        {
            var kli = new SECUR32.KERB_INTERACTIVE_LOGON()
            {
                MessageType = SECUR32.KERB_LOGON_SUBMIT_TYPE.KerbInteractiveLogon,
                UserName = "",
                Password = ""
            };
            IntPtr kerbLogInfo;
            SECUR32.LSA_STRING logonProc = new SECUR32.LSA_STRING
            {
                Buffer = Marshal.StringToHGlobalAuto("InstaLogon"),
                Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")),
                MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon"))
            };
            SECUR32.LSA_STRING originName = new SECUR32.LSA_STRING
            {
                Buffer = Marshal.StringToHGlobalAuto("InstaLogon"),
                Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")),
                MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon"))
            };
            SECUR32.LSA_STRING authPackage = new SECUR32.LSA_STRING
            {
                Buffer = Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A"),
                Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A")),
                MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A"))
            };
            IntPtr hLogonProc = Marshal.AllocHGlobal(Marshal.SizeOf(logonProc));
            Marshal.StructureToPtr(logonProc, hLogonProc, false);
            ADVAPI32.AllocateLocallyUniqueId(out IntPtr pluid);
            LsaConnectUntrusted(out IntPtr lsaHan);
            //SECUR32.LsaRegisterLogonProcess(hLogonProc, out lsaHan, out secMode);
            SECUR32.LsaLookupAuthenticationPackage(lsaHan, ref authPackage, out uint authPackID);

            kerbLogInfo = Marshal.AllocHGlobal(Marshal.SizeOf(kli));
            Marshal.StructureToPtr(kli, kerbLogInfo, false);

            var ts = new SECUR32.TOKEN_SOURCE("Insta");
            SECUR32.LsaLogonUser(
                lsaHan, 
                ref originName,
                SECUR32.SecurityLogonType.Interactive,
                authPackID, 
                kerbLogInfo,
                (uint)Marshal.SizeOf(kerbLogInfo),
                IntPtr.Zero,
                ref ts,
                out IntPtr profBuf,
                out uint profBufLen,
                out long logonID, 
                out IntPtr logonToken, 
                out QUOTA_LIMITS quotas,
                out WinStatusCodes subStatus);
        }
    }
}